#include "astro.h"

#include <future>
#include <iomanip>
#include <mutex>
#include <sstream>
#include <regex>

#include "base.pb.h"
#include "dwarf_utils.h"
#include "elog.h"
#include "elog_file.h"
#include "iTips.h"
#include "ws_response.h"
#include "params_setting.h"
#include "timer_utils.h"

#define TEST_MOSAIC 0

using namespace std;

static bool b_track_special_target = false;

std::atomic<StateNotify> Astro::capture_dark_state_ = STATE_IDLE;
std::atomic<StateNotify> Astro::capture_light_state_ = STATE_IDLE;

std::atomic<int> Astro::capture_light_to_take_ = 999;
std::atomic<int> Astro::capture_light_binning_ = 0;
std::atomic<int> Astro::capture_light_format_ = FITS;

std::mutex astro_mutex;
std::mutex shots_info_mutex;
std::mutex capture_light_state_mutex;
std::mutex capture_dark_state_mutex;
std::mutex capture_light_img_taken_mutex;
std::mutex capture_dark_progress_mutex;

static unordered_map<int, int (Astro::*)(WsPacket& ws_packet)> astro_func_table {
    { CMD_ASTRO_START_CALIBRATION, &Astro::calibration },
    { CMD_ASTRO_STOP_CALIBRATION, &Astro::stopCalibration },
    { CMD_ASTRO_START_GOTO_DSO, &Astro::gotoAndTrackDSO },
    { CMD_ASTRO_START_GOTO_SOLAR_SYSTEM, &Astro::gotoAndTrackSolarSystem },
    { CMD_ASTRO_STOP_GOTO, &Astro::stopGotoAndTracking },
    { CMD_ASTRO_START_CAPTURE_RAW_LIVE_STACKING, &Astro::captureRawLiveStacking },
    { CMD_ASTRO_STOP_CAPTURE_RAW_LIVE_STACKING, &Astro::stopCaptureRawLiveStacking },
    { CMD_ASTRO_START_CAPTURE_RAW_DARK, &Astro::captureDark },
    { CMD_ASTRO_STOP_CAPTURE_RAW_DARK, &Astro::stopCaptureDark },
    { CMD_ASTRO_CHECK_GOT_DARK, &Astro::checkGotDark },
    { CMD_ASTRO_GO_LIVE, &Astro::goLive },
    { CMD_ASTRO_START_TRACK_SPECIAL_TARGET, &Astro::trackSpecialTarget },
    { CMD_ASTRO_STOP_TRACK_SPECIAL_TARGET, &Astro::stopTrackingSpecialTarget },
    { CMD_ASTRO_START_ONE_CLICK_GOTO, &Astro::oneClickGoto },
};

void Astro::notifyAstroStateAndProgress() {
    calibrater_.notifyCalibraterState();
    gotoer_.notifyGotoerState();
    if (b_track_special_target)
        gotoer_.notifySpecialTrackerState();
    else
        gotoer_.notifyTrackerState();
    notifyCaptureLightStateAndProgress();
    notifyCaptureDarkStateAndProgress();
}

int Astro::handleMessage(WsPacket& ws_packet) {
    log_i("handle astro message, cmd = %d", ws_packet.cmd());

    //在表中查找函数指针并调用
    auto it = astro_func_table.find(ws_packet.cmd());
    if (it != astro_func_table.end()) {
        (this->*(it->second))(ws_packet);
    }
    else {
        log_i("Function not found");
    }

    return 0;
}

int Astro::panoMosaic() {
    capture_light_to_take_ = 15;
    capture_light_binning_ = 0;
    capture_light_format_ = 0;

    HourAngleCoordinates zenith_coord;
    calibrater_.getMyZenithCoord(zenith_coord);

    std::vector<EquatorialCoordinates> panoCoordJ2000;
    // // Veil Nebula
    // panoCoordJ2000.push_back(EquatorialCoordinates(20.950278, 31.425556));
    // panoCoordJ2000.push_back(EquatorialCoordinates(20.775278, 31.425556));
    // panoCoordJ2000.push_back(EquatorialCoordinates(20.949167, 30.186667));
    // panoCoordJ2000.push_back(EquatorialCoordinates(20.776389, 30.186667));
    // NGC 7000
    panoCoordJ2000.push_back(EquatorialCoordinates(21.033611, 44.38));
    panoCoordJ2000.push_back(EquatorialCoordinates(20.824722, 44.38));
    panoCoordJ2000.push_back(EquatorialCoordinates(21.031389, 43.141111));
    panoCoordJ2000.push_back(EquatorialCoordinates(20.826667, 43.141111));

    int count = 0;
    std::string target_name = "NGC 7000";
    for (const auto& point : panoCoordJ2000) {
        gotoer_.setAndNotifyGotoerState(STATE_RUNNING);

        count++;
        std::ostringstream pano_target_name;
        pano_target_name << target_name << " " << count;

        DeepSkyObject deep_sky_object(point, zenith_coord, pano_target_name.str());
        std::thread goto_target([&]() {
            gotoAndTrackTarget(deep_sky_object);
        });
        goto_target.detach();

        while (gotoer_.getTrackerState() != STATE_RUNNING) 
            sleep(1);

        cam_tele_.setExp(15.0);
        cam_tele_.setGain(80);
        cam_tele_.setIrCut(1);

        sleep(45);

        setAndNotifyCaptureLightState(STATE_RUNNING);
    
        captureRawLiveStacking(capture_light_binning_, capture_light_to_take_, capture_light_format_);

        setAndNotifyCaptureLightState(STATE_IDLE);

        cam_tele_.openCamera(0);
        usleep(500 * 1000);
        cam_tele_.setExp(1.0);
        cam_tele_.setGain(80);
        cam_tele_.setIrCut(1);

        gotoer_.stopGotoAndTracking();
        while (gotoer_.getTrackerState() != STATE_IDLE)
            usleep(100 * 1000);
    }

    return 0;
}

int Astro::oneClickGoto(WsPacket& ws_packet) {
    std::vector<HorizontalCoordinates> calibration_path;
    sky_seacher_.start(calibration_path);
    calibrater_.start(calibration_path);
    // notifyAstroStateAndProgress();
    return 0;
}

int Astro::calibration(WsPacket& ws_packet) {
    std::unique_lock<std::mutex> lock(astro_mutex);

    log_i("calibration");

    if (calibrater_.getCalibraterState() != STATE_IDLE || gotoer_.getGotoerState() != STATE_IDLE || gotoer_.getTrackerState() != STATE_IDLE ||
        capture_dark_state_ != STATE_IDLE || capture_light_state_ != STATE_IDLE) {

        if (calibrater_.getCalibraterState() != STATE_IDLE)
            itips_observer_->updateCmdIdAndErrorCode(CMD_ASTRO_START_CALIBRATION, CODE_ASTRO_CALIBRATION_RUNNING);
        else if (gotoer_.getGotoerState() != STATE_IDLE || gotoer_.getTrackerState() != STATE_IDLE)
            itips_observer_->updateCmdIdAndErrorCode(CMD_ASTRO_START_CALIBRATION, CODE_ASTRO_GOTO_RUNNING);
        else if (capture_dark_state_ != STATE_IDLE)
            itips_observer_->updateCmdIdAndErrorCode(CMD_ASTRO_START_CALIBRATION, CODE_ASTRO_DARK_RUNNING);
        else if (capture_light_state_ != STATE_IDLE)
            itips_observer_->updateCmdIdAndErrorCode(CMD_ASTRO_START_CALIBRATION, CODE_ASTRO_LIVE_STACKING_RUNNING);
        
        wsCommonResponse(CMD_ASTRO_START_CALIBRATION, MODULE_ASTRO, CMD_TYPE_RESPONSE, -CODE_ASTRO_FUNCTION_BUSY);
        return -CODE_ASTRO_FUNCTION_BUSY;
    }

    calibrater_.setAndNotifyCalibraterState(STATE_RUNNING);

    itips_observer_->updateCmdIdAndErrorCode(CMD_ASTRO_START_CALIBRATION, -1);

    lock.unlock();

    rgb_power_.setRgbEffect(RGB_EFFECT_ASTRO_CALIBRATION_SETP1);// 强制显示校准第一步灯效，用WORKING_STATE的话success_times_还没清

    int ret = calibration();

    if (ret != 0) 
        rgb_power_.setRgbEffect(RGB_EFFECT_ASTRO_FAIL);
    else 
        rgb_power_.setRgbEffect(RGB_EFFECT_ASTRO_SUCCESS);

    return ret;
}

int Astro::calibration() {
    return calibrater_.start();
}

int Astro::stopCalibration(WsPacket& ws_packet) {
    std::unique_lock<std::mutex> lock(astro_mutex);

    log_i("stopCalibration");

    int ret = stopCalibration();

    return ret;
}

int Astro::stopCalibration() {
    return calibrater_.stop();
}

int Astro::gotoAndTrackDSO(WsPacket& ws_packet) {
    std::unique_lock<std::mutex> lock(astro_mutex);

    log_i("gotoAndTrackDSO");

    ReqGotoDSO req_goto_dso;
    string serialized_data = ws_packet.data();

    if (!req_goto_dso.ParseFromString(serialized_data)) {
        wsCommonResponse(CMD_ASTRO_START_GOTO_DSO, MODULE_ASTRO, CMD_TYPE_RESPONSE, -WS_PARSE_PROTOBUF_ERROR);
        return -WS_PARSE_PROTOBUF_ERROR;
    }

    if (calibrater_.getCalibraterState() != STATE_IDLE || gotoer_.getGotoerState() != STATE_IDLE ||
        capture_dark_state_ != STATE_IDLE || capture_light_state_ != STATE_IDLE) {

        if (calibrater_.getCalibraterState() != STATE_IDLE)
            itips_observer_->updateCmdIdAndErrorCode(CMD_ASTRO_START_GOTO_DSO, CODE_ASTRO_CALIBRATION_RUNNING);
        else if (gotoer_.getGotoerState() != STATE_IDLE)
            itips_observer_->updateCmdIdAndErrorCode(CMD_ASTRO_START_GOTO_DSO, CODE_ASTRO_GOTO_RUNNING);
        else if (capture_dark_state_ != STATE_IDLE)
            itips_observer_->updateCmdIdAndErrorCode(CMD_ASTRO_START_GOTO_DSO, CODE_ASTRO_DARK_RUNNING);
        else if (capture_light_state_ != STATE_IDLE)
            itips_observer_->updateCmdIdAndErrorCode(CMD_ASTRO_START_GOTO_DSO, CODE_ASTRO_LIVE_STACKING_RUNNING);
        
        wsCommonResponse(CMD_ASTRO_START_GOTO_DSO, MODULE_ASTRO, CMD_TYPE_RESPONSE, -CODE_ASTRO_FUNCTION_BUSY);
        return -CODE_ASTRO_FUNCTION_BUSY;
    } else if (gotoer_.getTrackerState() != STATE_IDLE) {
        if (b_track_special_target)
            gotoer_.stopTrackingSpecial();
        else
            gotoer_.stopGotoAndTracking();
        while (gotoer_.getTrackerState() != STATE_IDLE)
            usleep(100 * 1000);
    }

    gotoer_.setAndNotifyGotoerState(STATE_RUNNING);

    itips_observer_->updateCmdIdAndErrorCode(CMD_ASTRO_START_GOTO_DSO, -1);

    lock.unlock();

    rgb_power_.setRgbEffect(RGB_EFFECT_WORKING_STATE);

    log_i("goto: %s(%f, %f)", req_goto_dso.target_name().c_str(), req_goto_dso.ra(), req_goto_dso.dec());

    // 设置曝光增益IRCUT
    cam_tele_.setExp(1.0);
    cam_tele_.setGain(80);
    cam_tele_.setIrCut(1);

#if TEST_MOSAIC
    if (req_goto_dso.target_name().find("7000") == -1) {
        EquatorialCoordinates target_eq_coord_J2000 = { req_goto_dso.ra(), req_goto_dso.dec() };

        HourAngleCoordinates zenith_coord;
        calibrater_.getMyZenithCoord(zenith_coord);

        DeepSkyObject deep_sky_object(target_eq_coord_J2000, zenith_coord, req_goto_dso.target_name());
        int ret = gotoAndTrackTarget(deep_sky_object);

        return ret;
    } else {
        panoMosaic();
        return 0;
    }
#else
    EquatorialCoordinates target_eq_coord_J2000 = { req_goto_dso.ra(), req_goto_dso.dec() };

    HourAngleCoordinates zenith_coord;
    calibrater_.getMyZenithCoord(zenith_coord);

    // 没有校准不能GOTO
    if (zenith_coord.ha == 0.0) {
        itips_observer_->updateCmdIdAndErrorCode(CMD_ASTRO_START_GOTO_DSO, CODE_ASTRO_NEED_CALIBRATION);
        wsCommonResponse(CMD_ASTRO_START_GOTO_DSO, MODULE_ASTRO, CMD_TYPE_RESPONSE, -CODE_ASTRO_GOTO_FAILED);
        gotoer_.setAndNotifyGotoerState(STATE_STOPPED);
        gotoer_.setAndNotifyGotoerState(STATE_IDLE);

        rgb_power_.setRgbEffect(RGB_EFFECT_ASTRO_FAIL);

        return -CODE_ASTRO_NEED_CALIBRATION;
    }

    DeepSkyObject deep_sky_object(target_eq_coord_J2000, zenith_coord, req_goto_dso.target_name());
    int ret = gotoAndTrackTarget(deep_sky_object);

    if (ret != 0) {
        rgb_power_.setRgbEffect(RGB_EFFECT_ASTRO_FAIL);
        if (ret == -CODE_STEP_MOTOR_LIMIT_POSITION_HITTED) 
            if (capture_light_state_ != STATE_IDLE)
                stopCaptureRawLiveStacking();// 跟星撞限位，停止抓图
    } else {
        rgb_power_.setRgbEffect(RGB_EFFECT_ASTRO_SUCCESS);
    }

    return ret;
#endif
}

int Astro::gotoAndTrackTarget(const DeepSkyObject& target) {
    HorizontalCoordinates lastMechanicalCoord;
    HorizontalCoordinates lastMyCoord;
    
    calibrater_.getLastMechanicalCoord(lastMechanicalCoord);
    calibrater_.getLastMyCoord(lastMyCoord);

    gotoer_.setDeepSkyObject(target);
    gotoer_.setLastMechanicalCoord(lastMechanicalCoord);
    gotoer_.setLastMyCoord(lastMyCoord);

    gotoer_.setReverseMotorYawMechanicalDirection(calibrater_.getReverseMotorYawMechanicalDirection());
    gotoer_.setReverseMotorPitchMechanicalDirection(calibrater_.getReverseMotorPitchMechanicalDirection());

    return gotoer_.gotoAndTarckTarget();
}

int Astro::gotoAndTrackSolarSystem(WsPacket& ws_packet) {
    std::unique_lock<std::mutex> lock(astro_mutex);

    log_i("gotoAndTrackSolarSystem");

    ReqGotoSolarSystem req_goto_solar_system;
    string serialized_data = ws_packet.data();

    if (!req_goto_solar_system.ParseFromString(serialized_data)) {
        wsCommonResponse(CMD_ASTRO_START_GOTO_SOLAR_SYSTEM, MODULE_ASTRO, CMD_TYPE_RESPONSE, -WS_PARSE_PROTOBUF_ERROR);
        return -WS_PARSE_PROTOBUF_ERROR;
    }

    log_i("index:%d, target_name:%s, lon:%f, lat:%f", req_goto_solar_system.index(), req_goto_solar_system.target_name().c_str(), req_goto_solar_system.lon() , req_goto_solar_system.lat());

    if (req_goto_solar_system.index() < Mercury || req_goto_solar_system.index() > Earth) {
        wsCommonResponse(CMD_ASTRO_START_GOTO_SOLAR_SYSTEM, MODULE_ASTRO, CMD_TYPE_RESPONSE, -WS_INVAID_PARAM);
        return -1;
    }

    if (calibrater_.getCalibraterState() != STATE_IDLE || gotoer_.getGotoerState() != STATE_IDLE ||
        capture_dark_state_ != STATE_IDLE || capture_light_state_ != STATE_IDLE) {
        
        if (calibrater_.getCalibraterState() != STATE_IDLE)
            itips_observer_->updateCmdIdAndErrorCode(CMD_ASTRO_START_GOTO_SOLAR_SYSTEM, CODE_ASTRO_CALIBRATION_RUNNING);
        else if (gotoer_.getGotoerState() != STATE_IDLE)
            itips_observer_->updateCmdIdAndErrorCode(CMD_ASTRO_START_GOTO_SOLAR_SYSTEM, CODE_ASTRO_GOTO_RUNNING);
        else if (capture_dark_state_ != STATE_IDLE)
            itips_observer_->updateCmdIdAndErrorCode(CMD_ASTRO_START_GOTO_SOLAR_SYSTEM, CODE_ASTRO_DARK_RUNNING);
        else if (capture_light_state_ != STATE_IDLE)
            itips_observer_->updateCmdIdAndErrorCode(CMD_ASTRO_START_GOTO_SOLAR_SYSTEM, CODE_ASTRO_LIVE_STACKING_RUNNING);

        wsCommonResponse(CMD_ASTRO_START_GOTO_SOLAR_SYSTEM, MODULE_ASTRO, CMD_TYPE_RESPONSE, -CODE_ASTRO_FUNCTION_BUSY);
        return -CODE_ASTRO_FUNCTION_BUSY;
    } else if (gotoer_.getTrackerState() != STATE_IDLE) {
        gotoer_.stopGotoAndTracking();
        while (gotoer_.getTrackerState() != STATE_IDLE)
            usleep(100 * 1000);
    }

    gotoer_.setAndNotifyGotoerState(STATE_RUNNING);

    itips_observer_->updateCmdIdAndErrorCode(CMD_ASTRO_START_GOTO_SOLAR_SYSTEM, -1);

    lock.unlock();

    rgb_power_.setRgbEffect(RGB_EFFECT_WORKING_STATE);

    HourAngleCoordinates zenith_coord;
    calibrater_.getMyZenithCoord(zenith_coord);

    if (req_goto_solar_system.index() == Sun || req_goto_solar_system.index() == EarthsMoon) {
        zenith_coord.ha = -req_goto_solar_system.lon();// 南地平坐标
        zenith_coord.dec = req_goto_solar_system.lat();
    }

    // 没有校准不能GOTO
    if (zenith_coord.ha == 0.0) {
        itips_observer_->updateCmdIdAndErrorCode(CMD_ASTRO_START_GOTO_SOLAR_SYSTEM, CODE_ASTRO_NEED_CALIBRATION);
        wsCommonResponse(CMD_ASTRO_START_GOTO_SOLAR_SYSTEM, MODULE_ASTRO, CMD_TYPE_RESPONSE, -CODE_ASTRO_GOTO_FAILED);
        gotoer_.setAndNotifyGotoerState(STATE_STOPPED);
        gotoer_.setAndNotifyGotoerState(STATE_IDLE);

        rgb_power_.setRgbEffect(RGB_EFFECT_ASTRO_FAIL);

        return -CODE_ASTRO_NEED_CALIBRATION;
    }

    GeocentricCoordinates gps_position = { req_goto_solar_system.lon() , req_goto_solar_system.lat() };
    gotoer_.setGpsPosition(gps_position);

    SolarGalaxyObject solar_galaxy_object((SolarSystemObjectIndex)req_goto_solar_system.index(), zenith_coord, req_goto_solar_system.target_name());
    int ret = gotoAndTrackTarget(solar_galaxy_object);

    if (ret != 0) {
        rgb_power_.setRgbEffect(RGB_EFFECT_ASTRO_FAIL);
        if (ret == -CODE_STEP_MOTOR_LIMIT_POSITION_HITTED) 
            if (capture_light_state_ != STATE_IDLE)
                stopCaptureRawLiveStacking();// 跟星撞限位，停止抓图
    } else {
        rgb_power_.setRgbEffect(RGB_EFFECT_ASTRO_SUCCESS);
    }

    return ret;
}

int Astro::gotoAndTrackTarget(const SolarGalaxyObject& target) {
    HorizontalCoordinates lastMechanicalCoord;
    HorizontalCoordinates lastMyCoord;
    
    calibrater_.getLastMechanicalCoord(lastMechanicalCoord);
    calibrater_.getLastMyCoord(lastMyCoord);

    gotoer_.setSolarGalaxyObject(target);
    gotoer_.setLastMechanicalCoord(lastMechanicalCoord);
    gotoer_.setLastMyCoord(lastMyCoord);

    std::string target_name;
    gotoer_.getTargetName(target_name);
    log_i("goto target:%s", target_name.c_str());

    if (target_name == "Sun" || target_name == "Moon") 
        return gotoer_.trackTargetConsiderBacklash();

    return gotoer_.gotoAndTarckTarget();
}

int Astro::stopGotoAndTracking(WsPacket& ws_packet) {
    std::unique_lock<std::mutex> lock(astro_mutex);

    log_i("stopGotoAndTracking");

    int ret = stopGotoAndTracking();

    return ret;
}

int Astro::stopGotoAndTracking() {
    return gotoer_.stopGotoAndTracking();
}

int Astro::trackSpecialTarget(WsPacket& ws_packet) {
    std::unique_lock<std::mutex> lock(astro_mutex);

    log_i("trackSpecialTarget");

    ReqTrackSpecialTarget req_track_special_target;
    string serialized_data = ws_packet.data();

    if (!req_track_special_target.ParseFromString(serialized_data)) {
        wsCommonResponse(CMD_ASTRO_START_TRACK_SPECIAL_TARGET, MODULE_ASTRO, CMD_TYPE_RESPONSE, -WS_PARSE_PROTOBUF_ERROR);
        return -WS_PARSE_PROTOBUF_ERROR;
    }

    log_i("index:%d, lon:%f, lat:%f", req_track_special_target.index(), req_track_special_target.lon() , req_track_special_target.lat());

    if (req_track_special_target.index() < 0 || req_track_special_target.index() > 1) {
        wsCommonResponse(CMD_ASTRO_START_TRACK_SPECIAL_TARGET, MODULE_ASTRO, CMD_TYPE_RESPONSE, -WS_INVAID_PARAM);
        return -1;
    }

    if (calibrater_.getCalibraterState() != STATE_IDLE || gotoer_.getGotoerState() != STATE_IDLE ||
        capture_dark_state_ != STATE_IDLE || capture_light_state_ != STATE_IDLE) {
        
        wsCommonResponse(CMD_ASTRO_START_TRACK_SPECIAL_TARGET, MODULE_ASTRO, CMD_TYPE_RESPONSE, -CODE_ASTRO_FUNCTION_BUSY);
        return -CODE_ASTRO_FUNCTION_BUSY;
    } else if (gotoer_.getTrackerState() != STATE_IDLE) {
        if (b_track_special_target)
            gotoer_.stopTrackingSpecial();
        else
            gotoer_.stopGotoAndTracking();
        while (gotoer_.getTrackerState() != STATE_IDLE)
            usleep(100 * 1000);
    }

    HourAngleCoordinates zenith_coord;
    zenith_coord.ha = -req_track_special_target.lon();// 南地平坐标
    zenith_coord.dec = req_track_special_target.lat();

    GeocentricCoordinates gps_position = { req_track_special_target.lon() , req_track_special_target.lat() };
    gotoer_.setGpsPosition(gps_position);

    SolarGalaxyObject solar_galaxy_object;
    if (req_track_special_target.index() == 0) 
        solar_galaxy_object = SolarGalaxyObject(Sun, zenith_coord, "Sun");
    else if (req_track_special_target.index() == 1) 
        solar_galaxy_object = SolarGalaxyObject(EarthsMoon, zenith_coord, "Moon");

    gotoer_.setSolarGalaxyObject(solar_galaxy_object);
    gotoer_.setAndNotifySpecialTrackerState(STATE_RUNNING);

    b_track_special_target = true;

    lock.unlock();

    rgb_power_.setRgbEffect(RGB_EFFECT_WORKING_STATE);

    int ret = gotoer_.trackSpecialTarget();

    if (ret != 0) {
        rgb_power_.setRgbEffect(RGB_EFFECT_ASTRO_FAIL);
        if (ret == -CODE_STEP_MOTOR_LIMIT_POSITION_HITTED) 
            if (capture_light_state_ != STATE_IDLE)
                stopCaptureRawLiveStacking();// 跟星撞限位，停止抓图
    } else {
        rgb_power_.setRgbEffect(RGB_EFFECT_ASTRO_SUCCESS);
    }

    b_track_special_target = false;
    
    return ret;
}

int Astro::stopTrackingSpecialTarget(WsPacket& ws_packet) {
    std::unique_lock<std::mutex> lock(astro_mutex);

    log_i("stopTrackingSpecialTarget");

    int ret = gotoer_.stopTrackingSpecial();

    return ret;
}

int Astro::captureRawLiveStacking(WsPacket& ws_packet) {
    std::unique_lock<std::mutex> lock(astro_mutex);

    log_i("captureRawLiveStacking");

    if (calibrater_.getCalibraterState() != STATE_IDLE || gotoer_.getGotoerState() != STATE_IDLE ||
        capture_dark_state_ != STATE_IDLE || capture_light_state_ == STATE_RUNNING || capture_light_state_ == STATE_STOPPING) {
        
        if (calibrater_.getCalibraterState() != STATE_IDLE)
            itips_observer_->updateCmdIdAndErrorCode(CMD_ASTRO_START_CAPTURE_RAW_LIVE_STACKING, CODE_ASTRO_CALIBRATION_RUNNING);
        else if (gotoer_.getGotoerState() != STATE_IDLE)
            itips_observer_->updateCmdIdAndErrorCode(CMD_ASTRO_START_CAPTURE_RAW_LIVE_STACKING, CODE_ASTRO_GOTO_RUNNING);
        else if (capture_dark_state_ != STATE_IDLE)
            itips_observer_->updateCmdIdAndErrorCode(CMD_ASTRO_START_CAPTURE_RAW_LIVE_STACKING, CODE_ASTRO_DARK_RUNNING);
        else if (capture_light_state_ == STATE_RUNNING || capture_light_state_ == STATE_STOPPING)
            itips_observer_->updateCmdIdAndErrorCode(CMD_ASTRO_START_CAPTURE_RAW_LIVE_STACKING, CODE_ASTRO_LIVE_STACKING_RUNNING);        

        wsCommonResponse(CMD_ASTRO_START_CAPTURE_RAW_LIVE_STACKING, MODULE_ASTRO, CMD_TYPE_RESPONSE, -CODE_ASTRO_FUNCTION_BUSY);
        return -CODE_ASTRO_FUNCTION_BUSY;
    }

    setAndNotifyCaptureLightState(STATE_RUNNING);

    itips_observer_->updateCmdIdAndErrorCode(CMD_ASTRO_START_CAPTURE_RAW_LIVE_STACKING, -1);

    lock.unlock();

    rgb_power_.setRgbEffect(RGB_EFFECT_WORKING_STATE);

    ExposureSetting exp_setting;
    cam_tele_.getExp(exp_setting);

    if (exp_setting.value < 1) {
        capture_light_binning_ = 0;
        WsPacket ws_packet = WsPacket();
        cam_tele_.getAllFeatureParams(ws_packet);
    }

    log_i("capture_light_binning_:%d, capture_light_to_take_:%d, capture_light_format_:%d", capture_light_binning_.load(), capture_light_to_take_.load(), capture_light_format_.load());

    int ret = captureRawLiveStacking(capture_light_binning_, capture_light_to_take_, capture_light_format_);

    if (ret != 0)
        rgb_power_.setRgbEffect(RGB_EFFECT_ASTRO_FAIL);
    else
        rgb_power_.setRgbEffect(RGB_EFFECT_ASTRO_SUCCESS);

    return ret;
}

int Astro::captureRawLiveStacking(int binning, int img_to_take, int format) {
    if (cam_tele_.getPhotoState() != CAM_TELE_PHOTO_IDLE || cam_tele_.getRecordState() != CAM_RECORD_IDLE ||
        cam_tele_.getTimelapseState() != CAM_TIMELAPSE_IDLE || cam_tele_.getPanoramaState() != CAM_PANORAMA_IDLE) {
        log_e("cam tele is busy");

        if (cam_tele_.getPhotoState() != CAM_TELE_PHOTO_IDLE)
            itips_observer_->updateCmdIdAndErrorCode(CMD_ASTRO_START_CAPTURE_RAW_LIVE_STACKING, CODE_CAMERA_TELE_RUNNING_PHOTO);
        else if (cam_tele_.getRecordState() != CAM_RECORD_IDLE)
            itips_observer_->updateCmdIdAndErrorCode(CMD_ASTRO_START_CAPTURE_RAW_LIVE_STACKING, CODE_CAMERA_TELE_RUNNING_RECORD);
        else if (cam_tele_.getTimelapseState() != CAM_TIMELAPSE_IDLE)
            itips_observer_->updateCmdIdAndErrorCode(CMD_ASTRO_START_CAPTURE_RAW_LIVE_STACKING, CODE_CAMERA_TELE_RUNNING_TIMELAPSE);
        else if (cam_tele_.getPanoramaState() != CAM_PANORAMA_IDLE)
            itips_observer_->updateCmdIdAndErrorCode(CMD_ASTRO_START_CAPTURE_RAW_LIVE_STACKING, CODE_CAMERA_TELE_RUNNING_PANORAMA);

        wsCommonResponse(CMD_ASTRO_START_CAPTURE_RAW_LIVE_STACKING, MODULE_ASTRO, CMD_TYPE_RESPONSE, -CODE_CAMERA_TELE_WORKING_BUSY);
        setAndNotifyCaptureLightState(STATE_STOPPED);
        setAndNotifyCaptureLightState(STATE_IDLE);

        return -CODE_CAMERA_TELE_WORKING_BUSY;
    }

    if (checkSdExist() < 0) {
        log_e("check tfcard not exit");

        itips_observer_->updateCmdIdAndErrorCode(CMD_ASTRO_START_CAPTURE_RAW_LIVE_STACKING, WS_SDCARD_NOT_EXIST);

        wsCommonResponse(CMD_ASTRO_START_CAPTURE_RAW_LIVE_STACKING, MODULE_ASTRO, CMD_TYPE_RESPONSE, -WS_SDCARD_NOT_EXIST);
        setAndNotifyCaptureLightState(STATE_STOPPED);
        setAndNotifyCaptureLightState(STATE_IDLE);

        return -WS_SDCARD_NOT_EXIST;
    }

    // 获取拍摄目标名称
    string target_name;
#if !DEBUG_STACKING
    gotoer_.getTargetName(target_name);
#else
    target_name = "M51";
#endif
    log_i("target_name:%s", target_name.c_str());

    // 获取拍摄目标坐标
    EquatorialCoordinates taget_coord_j2000;
    gotoer_.getTargetCoordJ2000(taget_coord_j2000);

    // 获取用户设置的曝光增益
    ExposureSetting exp_setting;
    cam_tele_.getExp(exp_setting);

    IntValueSetting gain_setting;
    cam_tele_.getGain(gain_setting);

    // 没有goto就拍摄，提示错误
    if (!gotoer_.isGotoObject()) {
        itips_observer_->updateCmdIdAndErrorCode(CMD_ASTRO_START_CAPTURE_RAW_LIVE_STACKING, CODE_ASTRO_NEED_GOTO);
        wsCommonResponse(CMD_ASTRO_START_CAPTURE_RAW_LIVE_STACKING, MODULE_ASTRO, CMD_TYPE_RESPONSE, -CODE_ASTRO_NEED_GOTO);
    } else if (!gotoer_.isSolarGalaxyObject()) {
        if (exp_setting.value < 5 || gain_setting.value < 30 || gain_setting.value > 120) {
            itips_observer_->updateCmdIdAndErrorCode(CMD_ASTRO_START_CAPTURE_RAW_LIVE_STACKING, CODE_ASTRO_NEED_ADJUST_SHOOT_PARAM);
            wsCommonResponse(CMD_ASTRO_START_CAPTURE_RAW_LIVE_STACKING, MODULE_ASTRO, CMD_TYPE_RESPONSE, -CODE_ASTRO_NEED_ADJUST_SHOOT_PARAM);
        } else {
            std::vector<DarkFrameParameter> dark_lost;
            checkLostDark(dark_lost);

            for (const auto& elem : dark_lost) {
                if (gain_setting.value == elem.gain) {
                    itips_observer_->updateCmdIdAndErrorCode(CMD_ASTRO_START_CAPTURE_RAW_LIVE_STACKING, CODE_ASTRO_DARK_NOT_FOUND);
                    wsCommonResponse(CMD_ASTRO_START_CAPTURE_RAW_LIVE_STACKING, MODULE_ASTRO, CMD_TYPE_RESPONSE, -CODE_ASTRO_DARK_NOT_FOUND);
                    break;
                }
            }
        }
    }

    // 用于进度通知
    capture_light_img_taken_ = 0;
    capture_light_img_to_take_ = img_to_take;
    capture_light_exp_setting_index_ = exp_setting.index;
    capture_light_gain_setting_index_ = gain_setting.index;
    capture_light_target_name_ = target_name;

    wsResNotifyProgressCaptureRawLiveStacking(CMD_NOTIFY_PROGRASS_CAPTURE_RAW_LIVE_STACKING, MODULE_NOTIFY, CMD_TYPE_NOTIFICATION, capture_light_img_to_take_, ALL_COUNT, capture_light_img_taken_, 0, capture_light_exp_setting_index_, capture_light_gain_setting_index_, capture_light_target_name_);
    
    // 获取当前时间戳
    string timestamp = getTimeStamp();

#if !DEBUG_STACKING
    // 拼RAW文件夹路径
    std::ostringstream img_parent_path_ss;
    if (target_name.empty())
        img_parent_path_ss << album_path << astro_path << "DWARF_RAW_" << "EXP_" << exp_setting.value << "_GAIN_" << gain_setting.value << "_" << timestamp;
    else
        img_parent_path_ss << album_path << astro_path << "DWARF_RAW_" << target_name << "_EXP_" << exp_setting.value << "_GAIN_" << gain_setting.value << "_" << timestamp;

    std::string img_parent_path = img_parent_path_ss.str();
#else
    std::string img_parent_path = "/sdcard/DWARF_II/Astronomy/DWARF_RAW_M51_EXP_15_GAIN_50_2024-03-11-23-38-21-660";
    exp_setting.value = 15.0;
    gain_setting.value = 50;
#endif

    log_i("img_parent_path_ss:%s", img_parent_path.c_str());

    // 创建RAW文件夹路径
    if (!std::filesystem::exists(img_parent_path)) {
        try {
            std::filesystem::create_directory(img_parent_path);
        } catch (const std::filesystem::filesystem_error& e) {
            log_i("std::filesystem::create_directory:%s", e.what());

            itips_observer_->updateCmdIdAndErrorCode(CMD_ASTRO_START_CAPTURE_RAW_LIVE_STACKING, WS_SDCARD_WRITE_ERROR);

            wsCommonResponse(CMD_ASTRO_START_CAPTURE_RAW_LIVE_STACKING, MODULE_ASTRO, CMD_TYPE_RESPONSE, -WS_SDCARD_WRITE_ERROR);
            setAndNotifyCaptureLightState(STATE_STOPPED);
            setAndNotifyCaptureLightState(STATE_IDLE);

            return -WS_SDCARD_WRITE_ERROR;
        }
    }

    std::string format_str;
    if (format == FITS)
        format_str = "FITS";
    else
        format_str = "TIFF";

    std::string ir_state;
    int ir_value = -1;
    cam_tele_.getIrCut(&ir_value);
    if (ir_value == 0)
        ir_state = "CUT";
    else if (ir_value > 0)
        ir_state = "PASS";

    std::string binning_str;
    if (binning == 0)
        binning_str = "1*1";
    else
        binning_str = "2*2";

    nlohmann::json shots_info = {
        {"RA", taget_coord_j2000.ra},
        {"DEC", taget_coord_j2000.dec},
        {"exp", exp_setting.name},
        {"gain", gain_setting.value},
        {"ir", ir_state},
        {"target", target_name},
        {"binning", binning_str},
        {"format", format_str},
        {"shotsToTake", img_to_take},
        {"shotsTaken", 0},
        {"shotsStacked", 0}
    };

    // 注册拍RAW图回调函数，进行转格式，并通知后期处理线程
    if (format != FITS) {
        cam_tele_.setCaptureRawCallback([&](const cv::Mat& img, const std::string& date_obs, int img_count) {
            std::ostringstream img_name;
            if (target_name.empty())
                img_name << "/raw_" << exp_setting.value << "s_" << gain_setting.value << "_" << std::setfill('0') << std::setw(4) << img_count << ".tif";
            else
                img_name << "/raw_" << target_name << "_" << exp_setting.value << "s_" << gain_setting.value << "_" << std::setfill('0') << std::setw(4) << img_count << ".tif";

#if !DEBUG_STACKING
            std::string img_path = img_parent_path + img_name.str();
            if (cv::imwrite(img_path, img)) {
                image_processor_.addImg(img_name.str());
                setAndNotifyCaptureLightImgTaken(img_count + 1);

                // system("sync");

                std::unique_lock<std::mutex> lock(shots_info_mutex);
                shots_info["shotsTaken"] = img_count + 1;

                return 0;
            } else {
                // 存图失败返回，停止拍摄和叠图
                itips_observer_->updateCmdIdAndErrorCode(CMD_ASTRO_START_CAPTURE_RAW_LIVE_STACKING, WS_SDCARD_WRITE_ERROR);

                wsCommonResponse(CMD_ASTRO_START_CAPTURE_RAW_LIVE_STACKING, MODULE_ASTRO, CMD_TYPE_RESPONSE, -WS_SDCARD_WRITE_ERROR);
                stopCaptureRawLiveStacking();

                setAndNotifyCaptureLightState(STATE_STOPPING);

                return -WS_SDCARD_WRITE_ERROR;
            }
#else
            // std::ostringstream img_name_debug;
            // img_name_debug << "/" << std::setfill('0') << std::setw(4) << img_count << ".tif";

            // 拼RAW文件夹路径
            std::ostringstream img_parent_path_ss;
            if (target_name.empty())
                img_parent_path_ss << album_path << astro_path << "DWARF_RAW_" << "EXP_" << exp_setting.value << "_GAIN_" << gain_setting.value << "_" << timestamp;
            else
                img_parent_path_ss << album_path << astro_path << "DWARF_RAW_" << target_name << "_EXP_" << exp_setting.value << "_GAIN_" << gain_setting.value << "_" << timestamp;

            std::string img_parent_path_debug = img_parent_path_ss.str();
            std::filesystem::create_directory(img_parent_path_debug);

            std::string img_path = img_parent_path_debug + img_name.str();
            if (cv::imwrite(img_path, img)) {
                image_processor_.addImg(img_name.str());
                setAndNotifyCaptureLightImgTaken(img_count + 1);

                std::unique_lock<std::mutex> lock(shots_info_mutex);
                shots_info["shotsTaken"] = img_count + 1;

                return 0;
            } else {
                // 存图失败返回，停止拍摄和叠图
                wsCommonResponse(CMD_ASTRO_START_CAPTURE_RAW_LIVE_STACKING, MODULE_ASTRO, CMD_TYPE_RESPONSE, -WS_SDCARD_WRITE_ERROR);
                stopCaptureRawLiveStacking();

                setAndNotifyCaptureLightState(STATE_STOPPING);

                return -WS_SDCARD_WRITE_ERROR;
            }
#endif
        });
    } else {
        cam_tele_.setCaptureRawCallback([&](const cv::Mat& img, const std::string& date_obs, int img_count) {
            std::ostringstream img_name;
            if (target_name.empty())
                img_name << "/raw_" << exp_setting.value << "s_" << gain_setting.value << "_" << std::setfill('0') << std::setw(4) << img_count << ".fits";
            else
                img_name << "/raw_" << target_name << "_" << exp_setting.value << "s_" << gain_setting.value << "_" << std::setfill('0') << std::setw(4) << img_count << ".fits";

#if !DEBUG_STACKING
            std::string img_path = img_parent_path + img_name.str();
            if (0 == file_manager_.convertToFits(exp_setting.value, gain_setting.value, binning, taget_coord_j2000.ra, taget_coord_j2000.dec, target_name.c_str(), date_obs.c_str(), img, img_path.c_str())) {
                image_processor_.addImg(img_name.str());

                setAndNotifyCaptureLightImgTaken(img_count + 1);

                // system("sync");

                std::unique_lock<std::mutex> lock(shots_info_mutex);
                shots_info["shotsTaken"] = img_count + 1;

                return 0;
            } else {
                // 存图失败返回，停止拍摄和叠图
                itips_observer_->updateCmdIdAndErrorCode(CMD_ASTRO_START_CAPTURE_RAW_LIVE_STACKING, WS_SDCARD_WRITE_ERROR);

                wsCommonResponse(CMD_ASTRO_START_CAPTURE_RAW_LIVE_STACKING, MODULE_ASTRO, CMD_TYPE_RESPONSE, -WS_SDCARD_WRITE_ERROR);
                stopCaptureRawLiveStacking();

                setAndNotifyCaptureLightState(STATE_STOPPING);

                return -WS_SDCARD_WRITE_ERROR;
            }
#else
            // std::ostringstream img_name_debug;
            // img_name_debug << "/" << std::setfill('0') << std::setw(4) << img_count << ".fits";

            // 拼RAW文件夹路径
            std::ostringstream img_parent_path_ss;
            if (target_name.empty())
                img_parent_path_ss << album_path << astro_path << "DWARF_RAW_" << "EXP_" << exp_setting.value << "_GAIN_" << gain_setting.value << "_" << timestamp;
            else
                img_parent_path_ss << album_path << astro_path << "DWARF_RAW_" << target_name << "_EXP_" << exp_setting.value << "_GAIN_" << gain_setting.value << "_" << timestamp;

            std::string img_parent_path_debug = img_parent_path_ss.str();
            std::filesystem::create_directory(img_parent_path_debug);
            
            std::string img_path = img_parent_path_debug + img_name.str();
            if (0 == file_manager_.convertToFits(exp_setting.value, gain_setting.value, binning, taget_coord_j2000.ra, taget_coord_j2000.dec, target_name.c_str(), date_obs.c_str(), img, img_path.c_str())) {
                image_processor_.addImg(img_name.str());
                setAndNotifyCaptureLightImgTaken(img_count + 1);

                std::unique_lock<std::mutex> lock(shots_info_mutex);
                shots_info["shotsTaken"] = img_count + 1;

                return 0;
            } else {
                // 存图失败返回，停止拍摄和叠图
                wsCommonResponse(CMD_ASTRO_START_CAPTURE_RAW_LIVE_STACKING, MODULE_ASTRO, CMD_TYPE_RESPONSE, -WS_SDCARD_WRITE_ERROR);
                stopCaptureRawLiveStacking();

                setAndNotifyCaptureLightState(STATE_STOPPING);

                return -WS_SDCARD_WRITE_ERROR;
            }
#endif
        });
    }

    stopCaptureRawLiveStacking();

    if (live_stacking_thread_.valid()) 
        live_stacking_thread_.wait();

    if (capture_raw_thread_.valid()) 
        capture_raw_thread_.wait();

    // 先清空图像名称缓存
    image_processor_.popAllImg();

    // 保证还没开始可以停止
    if (capture_light_state_ == STATE_STOPPING) {
        if (itips_observer_->getErrorCode(CMD_ASTRO_START_CAPTURE_RAW_LIVE_STACKING) == -1)
            itips_observer_->backToOldCmdIdAndErrorCode(CMD_ASTRO_START_CAPTURE_RAW_LIVE_STACKING);

        wsCommonResponse(CMD_ASTRO_STOP_CAPTURE_RAW_LIVE_STACKING, MODULE_ASTRO, CMD_TYPE_RESPONSE, WS_OK);
        setAndNotifyCaptureLightState(STATE_STOPPED);

        album_.album_astro_.updateAstro(img_parent_path);

        return 0;
    }

    // 先开叠图线程
    live_stacking_thread_ = std::async(std::launch::async, [&]() {
        if (gotoer_.isSolarGalaxyObject())
            image_processor_.startLiveStackingSunMoonPipeline(img_to_take, exp_setting, gain_setting, binning, img_parent_path, shots_info, shots_info_mutex);
        else
            image_processor_.startLiveStackingPipeline(img_to_take, exp_setting, gain_setting, binning, img_parent_path, shots_info, shots_info_mutex);
    });
    
    // 再开拍RAW图线程
    capture_raw_thread_ = std::async(std::launch::async, [&]() {
        if (binning)
            return cam_tele_.startCaptureRaw(cam_tele_.imgSize2k(), img_to_take);
        else
            return cam_tele_.startCaptureRaw(cam_tele_.imgSize4k(), img_to_take);
    });

    int ret;

    if (live_stacking_thread_.valid()) 
        live_stacking_thread_.wait();

    if (capture_raw_thread_.valid()) 
        ret = capture_raw_thread_.get();

    if (ret != -WS_SDCARD_WRITE_ERROR) {
        if (capture_light_state_ == STATE_STOPPING) {
            if (itips_observer_->getErrorCode(CMD_ASTRO_START_CAPTURE_RAW_LIVE_STACKING) == -1)
                itips_observer_->backToOldCmdIdAndErrorCode(CMD_ASTRO_START_CAPTURE_RAW_LIVE_STACKING);
            wsCommonResponse(CMD_ASTRO_STOP_CAPTURE_RAW_LIVE_STACKING, MODULE_ASTRO, CMD_TYPE_RESPONSE, WS_OK);
        } else {
            if (itips_observer_->getErrorCode(CMD_ASTRO_START_CAPTURE_RAW_LIVE_STACKING) == -1)
                itips_observer_->updateCmdIdAndErrorCode(CMD_ASTRO_START_CAPTURE_RAW_LIVE_STACKING, WS_OK);     
            wsCommonResponse(CMD_ASTRO_START_CAPTURE_RAW_LIVE_STACKING, MODULE_ASTRO, CMD_TYPE_RESPONSE, WS_OK);
        }
    }

    setAndNotifyCaptureLightState(STATE_STOPPED);

    album_.album_astro_.updateAstro(img_parent_path);

    return ret;
    
}

int Astro::goLive(WsPacket& ws_packet) {
    std::unique_lock<std::mutex> lock(astro_mutex);

    if (capture_dark_state_ != STATE_IDLE || capture_light_state_ == STATE_RUNNING || capture_light_state_ == STATE_STOPPING) {
        if (capture_dark_state_ != STATE_IDLE)
            itips_observer_->updateCmdIdAndErrorCode(CMD_ASTRO_GO_LIVE, CODE_ASTRO_DARK_RUNNING);
        else if (capture_light_state_ == STATE_RUNNING || capture_light_state_ == STATE_STOPPING)
            itips_observer_->updateCmdIdAndErrorCode(CMD_ASTRO_GO_LIVE, CODE_ASTRO_LIVE_STACKING_RUNNING);     

        wsCommonResponse(CMD_ASTRO_GO_LIVE, MODULE_ASTRO, CMD_TYPE_RESPONSE, -CODE_ASTRO_FUNCTION_BUSY);
    } else if (capture_light_state_ == STATE_STOPPED) {
        setAndNotifyCaptureLightState(STATE_IDLE);// 只能先改状态，否则摄像头开不起来
        lock.unlock();

        log_i("open camera");

        // 获取用户设置的曝光增益IR状态
        ExposureSetting exp_setting;
        cam_tele_.getExp(exp_setting);

        IntValueSetting gain_setting;
        cam_tele_.getGain(gain_setting);

        int ir_value = -1;
        cam_tele_.getIrCut(&ir_value);

        int ret = cam_tele_.openCamera(0);
        cam_tele_.getAllParams();
        usleep(100 * 1000);
        std::string target_name;
        gotoer_.getTargetName(target_name);
        log_i("target_name:%s", target_name.c_str());
        if (target_name != "Moon" && target_name != "Sun") {
            cam_tele_.setExp(1.0);
            cam_tele_.setGain(80);
            cam_tele_.setIrCut(1);
        } else {
            cam_tele_.setExp(exp_setting.value);
            cam_tele_.setGain(gain_setting.value);
            cam_tele_.setIrCut(ir_value);
        }

        if (ret < 0) {
            itips_observer_->updateCmdIdAndErrorCode(CMD_ASTRO_GO_LIVE, ret);
        } else {
            if (itips_observer_->getErrorCode(CMD_ASTRO_GO_LIVE) == 0) // 之前的天文接口都没错误，GO LIVE后才提示成功，否则保持
                itips_observer_->updateCmdIdAndErrorCode(CMD_ASTRO_GO_LIVE, ret);
        } 

        wsCommonResponse(CMD_ASTRO_GO_LIVE, MODULE_ASTRO, CMD_TYPE_RESPONSE, ret);
    } else {
        wsCommonResponse(CMD_ASTRO_GO_LIVE, MODULE_ASTRO, CMD_TYPE_RESPONSE, WS_OK);

        setAndNotifyCaptureLightState(STATE_IDLE);
    }
    
    return 0;
}

int Astro::stopCaptureRawLiveStacking(WsPacket& ws_packet) {
    std::unique_lock<std::mutex> lock(astro_mutex);

    if (capture_light_state_ == STATE_RUNNING) 
        setAndNotifyCaptureLightState(STATE_STOPPING);
    else 
        setAndNotifyCaptureLightState(capture_light_state_);

    if (capture_light_state_ == STATE_IDLE)
        wsCommonResponse(CMD_ASTRO_STOP_CAPTURE_RAW_LIVE_STACKING, MODULE_ASTRO, CMD_TYPE_RESPONSE, WS_OK);

    log_i("stopCaptureRawLiveStacking");
    int ret = stopCaptureRawLiveStacking();

    return ret;
}

int Astro::stopCaptureRawLiveStacking() {
    cam_tele_.stopCaptureRaw();
    image_processor_.stopLiveStacking();

    return 0;
}

// 使用线程锁保护获取的状态和主动通知的状态之间的顺序，确保后面的状态不会先发送
void Astro::setAndNotifyCaptureLightState(const StateNotify& state) {
    std::unique_lock<std::mutex> lock(capture_light_state_mutex);

    capture_light_state_ = state;

    log_i("capture_light_state_:%d", capture_light_state_.load());

    wsResNotifyOperationState(CMD_NOTIFY_STATE_CAPTURE_RAW_LIVE_STACKING, MODULE_NOTIFY, CMD_TYPE_NOTIFICATION, capture_light_state_);
}

void Astro::notifyCaptureLightStateAndProgress() {
    std::unique_lock<std::mutex> lock(capture_light_state_mutex);

    if (capture_light_state_ != STATE_IDLE) {
        log_i("capture_light_state_:%d", capture_light_state_.load());
        wsResNotifyOperationState(CMD_NOTIFY_STATE_CAPTURE_RAW_LIVE_STACKING, MODULE_NOTIFY, CMD_TYPE_NOTIFICATION, capture_light_state_);

        if (capture_light_state_ != STATE_STOPPED) {
            notifyCaptureLightImgTaken();
            image_processor_.notifyStackCount();
        }
    }
}

void Astro::setAndNotifyCaptureLightImgTaken(int img_count) {
    std::lock_guard<std::mutex> lock(capture_light_img_taken_mutex);

    capture_light_img_taken_ = img_count;

    log_i("capture_light_img_to_take_:%d, capture_light_img_taken_:%d, capture_light_exp_setting_index_:%d, capture_light_gain_setting_index_:%d, capture_light_target_name_:%s", capture_light_img_to_take_, capture_light_img_taken_.load(), capture_light_exp_setting_index_, capture_light_gain_setting_index_, capture_light_target_name_.c_str());

    wsResNotifyProgressCaptureRawLiveStacking(CMD_NOTIFY_PROGRASS_CAPTURE_RAW_LIVE_STACKING, MODULE_NOTIFY, CMD_TYPE_NOTIFICATION, capture_light_img_to_take_, TAKEN_COUNT, capture_light_img_taken_, 0, capture_light_exp_setting_index_, capture_light_gain_setting_index_, capture_light_target_name_);
}

void Astro::notifyCaptureLightImgTaken() {
    std::lock_guard<std::mutex> lock(capture_light_img_taken_mutex);

    log_i("capture_light_img_to_take_:%d, capture_light_img_taken_:%d, capture_light_exp_setting_index_:%d, capture_light_gain_setting_index_:%d, capture_light_target_name_:%s", capture_light_img_to_take_, capture_light_img_taken_.load(), capture_light_exp_setting_index_, capture_light_gain_setting_index_, capture_light_target_name_.c_str());

    wsResNotifyProgressCaptureRawLiveStacking(CMD_NOTIFY_PROGRASS_CAPTURE_RAW_LIVE_STACKING, MODULE_NOTIFY, CMD_TYPE_NOTIFICATION, capture_light_img_to_take_, TAKEN_COUNT, capture_light_img_taken_, 0, capture_light_exp_setting_index_, capture_light_gain_setting_index_, capture_light_target_name_);
}

int Astro::captureDark(WsPacket& ws_packet) {
    std::unique_lock<std::mutex> lock(astro_mutex);

    log_i("captureDark");

    ReqCaptureDarkFrame req_capture_dark_frame;
    string serialized_data = ws_packet.data();

    if (!req_capture_dark_frame.ParseFromString(serialized_data)) {
        wsCommonResponse(CMD_ASTRO_START_CAPTURE_RAW_DARK, MODULE_ASTRO, CMD_TYPE_RESPONSE, -WS_PARSE_PROTOBUF_ERROR);
        return -WS_PARSE_PROTOBUF_ERROR;
    }

    if (calibrater_.getCalibraterState() != STATE_IDLE || gotoer_.getGotoerState() != STATE_IDLE ||
        capture_dark_state_ != STATE_IDLE || capture_light_state_ != STATE_IDLE) {

        if (calibrater_.getCalibraterState() != STATE_IDLE)
            itips_observer_->updateCmdIdAndErrorCode(CMD_ASTRO_START_CAPTURE_RAW_DARK, CODE_ASTRO_CALIBRATION_RUNNING);
        else if (gotoer_.getGotoerState() != STATE_IDLE)
            itips_observer_->updateCmdIdAndErrorCode(CMD_ASTRO_START_CAPTURE_RAW_DARK, CODE_ASTRO_GOTO_RUNNING);
        else if (capture_dark_state_ != STATE_IDLE)
            itips_observer_->updateCmdIdAndErrorCode(CMD_ASTRO_START_CAPTURE_RAW_DARK, CODE_ASTRO_DARK_RUNNING);
        else if (capture_light_state_ != STATE_IDLE)
            itips_observer_->updateCmdIdAndErrorCode(CMD_ASTRO_START_CAPTURE_RAW_DARK, CODE_ASTRO_LIVE_STACKING_RUNNING);
        
        wsCommonResponse(CMD_ASTRO_START_CAPTURE_RAW_DARK, MODULE_ASTRO, CMD_TYPE_RESPONSE, -CODE_ASTRO_FUNCTION_BUSY);
        return -CODE_ASTRO_FUNCTION_BUSY;
    }

    if (checkSdExist() < 0) {
        log_e("check tfcard not exit");
        wsCommonResponse(CMD_ASTRO_START_CAPTURE_RAW_DARK, MODULE_ASTRO, CMD_TYPE_RESPONSE, -WS_SDCARD_NOT_EXIST);
        
        return -WS_SDCARD_NOT_EXIST;
    }

    setAndNotifyCaptureDarkState(STATE_RUNNING);

    itips_observer_->updateCmdIdAndErrorCode(CMD_ASTRO_START_CAPTURE_RAW_DARK, -1);

    lock.unlock();

    rgb_power_.setRgbEffect(RGB_EFFECT_WORKING_STATE);

    log_i("req_capture_dark_frame.reshoot:%d", req_capture_dark_frame.reshoot());

    if (!std::filesystem::exists(sd_dark_path_)) {
        try {
            std::filesystem::create_directory(sd_dark_path_);
        } catch (const std::filesystem::filesystem_error& e) {
            log_i("std::filesystem::create_directory:%s", e.what());

            itips_observer_->updateCmdIdAndErrorCode(CMD_ASTRO_START_CAPTURE_RAW_DARK, WS_SDCARD_WRITE_ERROR);

            wsCommonResponse(CMD_ASTRO_START_CAPTURE_RAW_DARK, MODULE_ASTRO, CMD_TYPE_RESPONSE, -WS_SDCARD_WRITE_ERROR);
            setAndNotifyCaptureDarkState(STATE_STOPPED);
            setAndNotifyCaptureDarkState(STATE_IDLE);

            rgb_power_.setRgbEffect(RGB_EFFECT_ASTRO_FAIL);

            return -WS_SDCARD_WRITE_ERROR;
        }
    }

    if (!std::filesystem::exists(image_processor_.dark_folder_)) {
        try {
            std::filesystem::create_directory(image_processor_.dark_folder_);
        } catch (const std::filesystem::filesystem_error& e) {
            log_i("std::filesystem::create_directory:%s", e.what());

            itips_observer_->updateCmdIdAndErrorCode(CMD_ASTRO_START_CAPTURE_RAW_DARK, WS_SDCARD_WRITE_ERROR);

            wsCommonResponse(CMD_ASTRO_START_CAPTURE_RAW_DARK, MODULE_ASTRO, CMD_TYPE_RESPONSE, -WS_SDCARD_WRITE_ERROR);
            setAndNotifyCaptureDarkState(STATE_STOPPED);
            setAndNotifyCaptureDarkState(STATE_IDLE);

            rgb_power_.setRgbEffect(RGB_EFFECT_ASTRO_FAIL);

            return -WS_SDCARD_WRITE_ERROR;
        }
    }

    int ret = 0;
    if (req_capture_dark_frame.reshoot()) {
        string cmd_rm_all = "rm -rf " + sd_dark_path_ + "*";
        system(cmd_rm_all.c_str());

        cmd_rm_all = "rm -rf " + image_processor_.dark_folder_ + "*";
        system(cmd_rm_all.c_str());

        ret = captureDark(dark_lib_);
    } else {
        std::vector<DarkFrameParameter> dark_lost;
        checkLostDark(dark_lost);

        ret = captureDark(dark_lost);
    }

    if (ret != 0)
        rgb_power_.setRgbEffect(RGB_EFFECT_ASTRO_FAIL);
    else
        rgb_power_.setRgbEffect(RGB_EFFECT_ASTRO_SUCCESS);

    return 0;
}

int Astro::captureDark(const std::vector<DarkFrameParameter>& dark_lost) {
    if (cam_tele_.getPhotoState() != CAM_TELE_PHOTO_IDLE || cam_tele_.getRecordState() != CAM_RECORD_IDLE ||
        cam_tele_.getTimelapseState() != CAM_TIMELAPSE_IDLE || cam_tele_.getPanoramaState() != CAM_PANORAMA_IDLE) {
        log_e("cam tele is busy");

        if (cam_tele_.getPhotoState() != CAM_TELE_PHOTO_IDLE)
            itips_observer_->updateCmdIdAndErrorCode(CMD_ASTRO_START_CAPTURE_RAW_DARK, CODE_CAMERA_TELE_RUNNING_PHOTO);
        else if (cam_tele_.getRecordState() != CAM_RECORD_IDLE)
            itips_observer_->updateCmdIdAndErrorCode(CMD_ASTRO_START_CAPTURE_RAW_DARK, CODE_CAMERA_TELE_RUNNING_RECORD);
        else if (cam_tele_.getTimelapseState() != CAM_TIMELAPSE_IDLE)
            itips_observer_->updateCmdIdAndErrorCode(CMD_ASTRO_START_CAPTURE_RAW_DARK, CODE_CAMERA_TELE_RUNNING_TIMELAPSE);
        else if (cam_tele_.getPanoramaState() != CAM_PANORAMA_IDLE)
            itips_observer_->updateCmdIdAndErrorCode(CMD_ASTRO_START_CAPTURE_RAW_DARK, CODE_CAMERA_TELE_RUNNING_PANORAMA);

        wsCommonResponse(CMD_ASTRO_START_CAPTURE_RAW_DARK, MODULE_ASTRO, CMD_TYPE_RESPONSE, -CODE_CAMERA_TELE_WORKING_BUSY);
        setAndNotifyCaptureDarkState(STATE_STOPPED);
        setAndNotifyCaptureDarkState(STATE_IDLE);

        return -CODE_CAMERA_TELE_WORKING_BUSY;
    }

    if (checkSdExist() < 0) {
        log_e("check tfcard not exit");

        itips_observer_->updateCmdIdAndErrorCode(CMD_ASTRO_START_CAPTURE_RAW_DARK, WS_SDCARD_NOT_EXIST);

        wsCommonResponse(CMD_ASTRO_START_CAPTURE_RAW_DARK, MODULE_ASTRO, CMD_TYPE_RESPONSE, -WS_SDCARD_NOT_EXIST);
        setAndNotifyCaptureDarkState(STATE_STOPPED);
        setAndNotifyCaptureDarkState(STATE_IDLE);

        return -WS_SDCARD_NOT_EXIST;
    }

    // 创建RAW文件夹路径
    if (!std::filesystem::exists(sd_dark_path_)) {
        try {
            std::filesystem::create_directory(sd_dark_path_);
        } catch (const std::filesystem::filesystem_error& e) {
            log_i("std::filesystem::create_directory:%s", e.what());

            itips_observer_->updateCmdIdAndErrorCode(CMD_ASTRO_START_CAPTURE_RAW_DARK, WS_SDCARD_WRITE_ERROR);

            wsCommonResponse(CMD_ASTRO_START_CAPTURE_RAW_DARK, MODULE_ASTRO, CMD_TYPE_RESPONSE, -WS_SDCARD_WRITE_ERROR);
            setAndNotifyCaptureDarkState(STATE_STOPPED);
            setAndNotifyCaptureDarkState(STATE_IDLE);

            return -WS_SDCARD_WRITE_ERROR;
        }
    }

    int taken_count = (dark_lib_.size() - dark_lost.size()) * dark_count;
    int total_count = dark_lib_.size() * dark_count;
    int need_count = dark_lost.size() * dark_count;

    double progress = (double)taken_count / (double)total_count * 100.0;
    int remaining_time_s = need_count * 15.0;
    log_i("dark progress:%f, remaining_time_s:%d", progress, remaining_time_s);

    setAndNotifyCaptureDarkProgress(progress, remaining_time_s);

    stopCaptureDark();

    if (live_stacking_thread_.valid()) 
        live_stacking_thread_.wait();

    if (capture_raw_thread_.valid()) 
        capture_raw_thread_.wait();

    b_capture_dark_ = true;

    int ret;

    for (const auto& elem : dark_lost) {
        if (!b_capture_dark_)
            break;

        cam_tele_.setExp(elem.exp);
        cam_tele_.setGain(elem.gain);
        
        std::ostringstream img_sub_parent_path_ss;
        img_sub_parent_path_ss << sd_dark_path_ << "exp_" << elem.exp << "_gain_" << elem.gain << "_bin_" << elem.bin + 1 << "/";

        std::string img_sub_parent_path = img_sub_parent_path_ss.str();

        log_i("img_sub_parent_path:%s", img_sub_parent_path.c_str());

        if (!std::filesystem::exists(img_sub_parent_path)) {
            try {
                std::filesystem::create_directory(img_sub_parent_path);
            } catch (const std::filesystem::filesystem_error& e) {
                log_i("std::filesystem::create_directory:%s", e.what());

                itips_observer_->updateCmdIdAndErrorCode(CMD_ASTRO_START_CAPTURE_RAW_DARK, WS_SDCARD_WRITE_ERROR);

                wsCommonResponse(CMD_ASTRO_START_CAPTURE_RAW_DARK, MODULE_ASTRO, CMD_TYPE_RESPONSE, -WS_SDCARD_WRITE_ERROR);
                setAndNotifyCaptureDarkState(STATE_STOPPED);
                setAndNotifyCaptureDarkState(STATE_IDLE);

                return -WS_SDCARD_WRITE_ERROR;
            }
        } else {
            string cmd_rm_all = "rm -rf " + img_sub_parent_path + "*";
            system(cmd_rm_all.c_str());
        }

        cam_tele_.setCaptureRawCallback([&](const cv::Mat& img, const std::string& date_obs, int img_count) {
            if (!b_capture_dark_)
                return 0;

            std::ostringstream img_name;
            img_name << std::setfill('0') << std::setw(4) << img_count << ".fits";

            std::string img_path = img_sub_parent_path + img_name.str();
            log_i("img_path:%s", img_path.c_str());

            cv::Mat img_copy = img.clone();

            if (0 == file_manager_.convertToFits(elem.exp, elem.gain, elem.bin, 0, 0, NULL, date_obs.c_str(), img_copy, img_path.c_str())) {
                image_processor_.addImg(img_name.str());
                
                taken_count++;
                need_count--;

                progress = (double)taken_count / (double)total_count * 100.0;
                remaining_time_s = need_count * 15.0;
                log_i("dark progress:%f, remaining_time_s:%d", progress, remaining_time_s);

                setAndNotifyCaptureDarkProgress(progress, remaining_time_s);

                return 0;
            } else {
                // 存图失败返回，停止拍摄和叠图
                itips_observer_->updateCmdIdAndErrorCode(CMD_ASTRO_START_CAPTURE_RAW_DARK, WS_SDCARD_WRITE_ERROR);

                wsCommonResponse(CMD_ASTRO_START_CAPTURE_RAW_DARK, MODULE_ASTRO, CMD_TYPE_RESPONSE, -WS_SDCARD_WRITE_ERROR);
                stopCaptureDark();

                setAndNotifyCaptureDarkState(STATE_STOPPING);

                return -WS_SDCARD_WRITE_ERROR;
            }
        });

        // 先清空图像名称缓存
        image_processor_.popAllImg();

        // 先开叠图线程
        live_stacking_thread_ = std::async(std::launch::async, [&]() {
            image_processor_.startLiveStackingPipeline(dark_count, img_sub_parent_path, elem.exp, elem.gain, elem.bin);
            // image_processor_.startLiveStacking(dark_count, img_sub_parent_path, elem.exp, elem.gain, elem.bin);
        });
        
        // 再开拍RAW图线程
        capture_raw_thread_ = std::async(std::launch::async, [&]() {
            if (elem.bin)
                return cam_tele_.startCaptureRaw(cam_tele_.imgSize2k(), dark_count);
            else
                return cam_tele_.startCaptureRaw(cam_tele_.imgSize4k(), dark_count);
        });

        if (live_stacking_thread_.valid()) 
            live_stacking_thread_.wait();

        if (capture_raw_thread_.valid()) 
            ret = capture_raw_thread_.get();

        if (ret != 0)
            break;
    }

    if (ret != -WS_SDCARD_WRITE_ERROR) {
        if (b_capture_dark_ == false) {
            if (itips_observer_->getErrorCode(CMD_ASTRO_START_CAPTURE_RAW_DARK) == -1)
                itips_observer_->backToOldCmdIdAndErrorCode(CMD_ASTRO_START_CAPTURE_RAW_DARK);
            wsCommonResponse(CMD_ASTRO_STOP_CAPTURE_RAW_DARK, MODULE_ASTRO, CMD_TYPE_RESPONSE, WS_OK);
        } else {
            if (itips_observer_->getErrorCode(CMD_ASTRO_START_CAPTURE_RAW_DARK) == -1)
                itips_observer_->updateCmdIdAndErrorCode(CMD_ASTRO_START_CAPTURE_RAW_DARK, WS_OK);

            wsCommonResponse(CMD_ASTRO_START_CAPTURE_RAW_DARK, MODULE_ASTRO, CMD_TYPE_RESPONSE, WS_OK);
        }
    }

    setAndNotifyCaptureDarkState(STATE_STOPPED);
    setAndNotifyCaptureDarkState(STATE_IDLE);

    cam_tele_.openCamera(false);
    cam_tele_.getAllParams();

    return ret;
}

int Astro::stopCaptureDark(WsPacket& ws_packet) {
    std::unique_lock<std::mutex> lock(astro_mutex);

    if (capture_dark_state_ == STATE_RUNNING) 
        setAndNotifyCaptureDarkState(STATE_STOPPING);
    else 
        setAndNotifyCaptureDarkState(capture_dark_state_);

    if (capture_dark_state_ == STATE_IDLE)
        wsCommonResponse(CMD_ASTRO_STOP_CAPTURE_RAW_DARK, MODULE_ASTRO, CMD_TYPE_RESPONSE, WS_OK);

    log_i("stopCaptureDark");
    int ret = stopCaptureDark();

    return ret;
}

int Astro::stopCaptureDark() {
    b_capture_dark_ = false;
    cam_tele_.stopCaptureRaw();
    image_processor_.stopLiveStacking();

    return 0;
}

// 使用线程锁保护获取的状态和主动通知的状态之间的顺序，确保后面的状态不会先发送
void Astro::setAndNotifyCaptureDarkState(const StateNotify& state) {
    std::unique_lock<std::mutex> lock(capture_dark_state_mutex);

    capture_dark_state_ = state;

    log_i("capture_dark_state_:%d", capture_dark_state_.load());

    wsResNotifyOperationState(CMD_NOTIFY_STATE_CAPTURE_RAW_DARK, MODULE_NOTIFY, CMD_TYPE_NOTIFICATION, capture_dark_state_);
}

void Astro::notifyCaptureDarkStateAndProgress() {
    std::unique_lock<std::mutex> lock(capture_dark_state_mutex);

    if (capture_dark_state_ != STATE_IDLE) {
        log_i("capture_dark_state_:%d", capture_dark_state_.load());
        wsResNotifyOperationState(CMD_NOTIFY_STATE_CAPTURE_RAW_DARK, MODULE_NOTIFY, CMD_TYPE_NOTIFICATION, capture_dark_state_);

        notifyCaptureDarkProgress();
    }
}

void Astro::setAndNotifyCaptureDarkProgress(double progress, int remaining_time_s) {
    std::unique_lock<std::mutex> lock(capture_dark_progress_mutex);

    capture_dark_progress_ = progress;
    capture_dark_remaining_time_s = remaining_time_s;
    
    log_i("capture_dark_progress_:%f, capture_dark_remaining_time_s:%d", capture_dark_progress_.load(), capture_dark_remaining_time_s.load());

    wsResNotifyProgressCaptureRawDark(CMD_NOTIFY_PROGRASS_CAPTURE_RAW_DARK, MODULE_NOTIFY, CMD_TYPE_NOTIFICATION, int(capture_dark_progress_ * 100), capture_dark_remaining_time_s);
}

void Astro::notifyCaptureDarkProgress() {
    std::unique_lock<std::mutex> lock(capture_dark_progress_mutex);

    log_i("capture_dark_progress_:%f, capture_dark_remaining_time_s:%d", capture_dark_progress_.load(), capture_dark_remaining_time_s.load());

    wsResNotifyProgressCaptureRawDark(CMD_NOTIFY_PROGRASS_CAPTURE_RAW_DARK, MODULE_NOTIFY, CMD_TYPE_NOTIFICATION, int(capture_dark_progress_ * 100), capture_dark_remaining_time_s);
}

int Astro::checkGotDark(WsPacket& ws_packet) {
    std::vector<DarkFrameParameter> dark_got;
    int progress = checkGotDark(dark_got);
    if (progress < 0) {
        wsResCheckDarkFrame(CMD_ASTRO_CHECK_GOT_DARK, MODULE_ASTRO, CMD_TYPE_RESPONSE, WS_OK, 0);
        return 0;
    }

    // 百分数暗场进度
    double dark_progress = double(dark_got.size()) / double(dark_lib_.size()) * 100.0;
    log_i("dark progress:%f", dark_progress);

    // 返回保留两位小数
    wsResCheckDarkFrame(CMD_ASTRO_CHECK_GOT_DARK, MODULE_ASTRO, CMD_TYPE_RESPONSE, WS_OK, int(dark_progress * 100));

    return 0;
}

int Astro::checkGotDark(std::vector<DarkFrameParameter>& dark_got) {
    if (!std::filesystem::exists(image_processor_.dark_folder_)) 
        return -1;

    for (const auto& entry : std::filesystem::directory_iterator(image_processor_.dark_folder_)) {
        std::string img_name = entry.path().filename().string();

        // 定义正则表达式模式
        std::regex exp_pattern(R"(exp_([\d.]+)_gain_(\d+)_bin_(\d).png)");

        std::smatch matches;
        if (std::regex_match(img_name, matches, exp_pattern)) {
            DarkFrameParameter this_dark_para;
            this_dark_para.exp = std::stod(matches[1].str());
            this_dark_para.gain = std::stoi(matches[2].str());
            this_dark_para.bin = std::stoi(matches[3].str()) - 1;

            dark_got.push_back(this_dark_para);

            // 输出解析得到的值
            log_i("got dark: exp:%f, gain:%d, bin:%d", this_dark_para.exp, this_dark_para.gain, this_dark_para.bin);
        }
    }

    return 0;
}

int Astro::checkLostDark(std::vector<DarkFrameParameter>& dark_lost) {
    std::vector<DarkFrameParameter> dark_got;
    int ret = checkGotDark(dark_got);
    if (ret < 0) {
        dark_lost = dark_lib_;
        return 0;
    }

    for (const auto& elem : dark_lib_) {
        auto it = std::find(dark_got.begin(), dark_got.end(), elem);
        if (it == dark_got.end()) {
            dark_lost.push_back(elem);
            log_i("dark_lost: exp:%f, gain:%d, bin:%d", elem.exp, elem.gain, elem.bin);
        }
    }

    return 0;
}